package com.fx.securityConfig;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fx.demo.common.RsaTools;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PublicKey;
import java.util.List;

/**
 * des:
 *  负责校验token有效性，并从token中获取用户信息交给security
 * @author fxiao
 * @date 2020/12/15 12:54
 */
public class JwtFilter extends GenericFilterBean {
    private final Logger log= LoggerFactory.getLogger(this.getClass());
    /**
     * 两个作用：1，检测token有效性<br>
     *     2,如果token正确，则将token中的用户信息交给security去管理
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse rep=(HttpServletResponse)servletResponse;
        //这里不能将该token的名称叫authorization，否则会被zuul过滤掉
        String jwtToken = req.getHeader("jwt-token");
        if(!StringUtils.isEmpty(jwtToken)){
            PublicKey publicKey= RsaTools.parsePubKey(
                    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwsoT8bTU+S0hvOSxwVpqKl2BCwI63S0FH5x+vMlp2zNvnvR95JHJTOfii5Z2+oBz4yo28Ehjab44DLt59xKPlK3m4H/afO/cRLAyk1ULjWZ6r6lg0ZYAW9Khyu/eTr66eloA7ExcdtjQCWGxfZe16b0NmTjb9boS8qr9sCkBowODG3OgBKi+cfbD656Xz3DkIQIGm2UwLVVrcgM1uccGEBAJmTScfirICr7cwoZELFzKML1Sv/w5k1OQ6Zt+q1BfdfK30SoElj7NBc1CemnI3Wnj8iMMDvQ01HhW+3humQGQQnSsBXp0sEU4evom7i7libN8hnMaLmOYidInfs10QIDAQAB"
            );
            try {

                Claims claims = Jwts.parser()
                        .setSigningKey(publicKey)
                        .parseClaimsJws(jwtToken.replace("Bearer", ""))
                        .getBody();
                String username = claims.getSubject();//获取当前登录用户名
                List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
                UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, authorities);
                SecurityContextHolder.getContext().setAuthentication(token);
            }catch (ExpiredJwtException e) {
                writeToResponse(rep, "token过期，请重新登录!");
                log.info("token过期，请重新登录!");
                e.printStackTrace();
                return;
            }catch (UnsupportedJwtException e){
                writeToResponse(rep,"token错误或解析失败");
                log.info("token错误或解析失败");
                e.printStackTrace();
                return;
            }catch (SignatureException e){
                writeToResponse(rep,"当前token不可信");
                log.info("当前token不可信");
                e.printStackTrace();
                return;
            }
        }

        filterChain.doFilter(req, servletResponse);
    }

    /**
     * 写文本到response中
     * @param response
     * @param message
     */
    private void writeToResponse(HttpServletResponse response,String message){
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.write(new ObjectMapper().writeValueAsString(message));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            out.flush();
            out.close();
        }

    }

}
